﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Interface;
using Should;

namespace MugenInjectionTest.Core
{
    [TestClass]
    public partial class InjectorBinderTest : TestBase
    {
        [TestMethod]
        public void CreateContextTest()
        {
            using (MugenInjector injector = GetInjector())
            {
                var parameters = new IInjectionParameter[0];
                var dict = new Dictionary<string, object>();
                IBindingContext bindingContext = injector.CreateBinderContext(ServiceType, TypeInto, ParameterInfo,
                                                                              parameters, dict);
                bindingContext.Injector.ShouldEqual(injector);
                bindingContext.CallInjector.ShouldEqual(injector);
                bindingContext.Service.ShouldEqual(ServiceType);
                bindingContext.TypeInto.ShouldEqual<object>(TypeInto);
                bindingContext.Member.ShouldEqual(TypeInto);
                bindingContext.ParameterInfo.ShouldEqual(ParameterInfo);
                bindingContext.Parameters.ShouldEqual(parameters);
                bindingContext.SpecialParameters.ShouldEqual(dict);
                TestUtils.Catch<ArgumentNullException>(() => injector.CreateBinderContext(null, null, null, null, null));
            }
        }

        [TestMethod]
        public void NonGenericUnbindWithParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            Type bindType = typeof(string);

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(context => context.Parameters == parameters);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, new IInjectionParameter[1]);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindWithSpecialParametersTest()
        {
            var dict = new Dictionary<string, object>();
            Type bindType = typeof(string);

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(context => context.SpecialParameters == dict);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindWithSpecialParametersAndParametersTest()
        {
            var dict = new Dictionary<string, object>();
            Type bindType = typeof(string);
            var parameters = new IInjectionParameter[1];

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(
                    context => context.SpecialParameters == dict && context.Parameters == parameters);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindNamedBindingTest()
        {
            Type bindType = typeof(string);
            const string name = "test";

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, name);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindNamedBindingAndSpecialParametersTest()
        {
            Type bindType = typeof(string);
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                        .When(context => context.SpecialParameters == dict)
                        .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, name);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, name, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindNamedBindingAndSpecialParametersAndParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            Type bindType = typeof(string);
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                    .When(context => context.SpecialParameters == dict && context.Parameters == parameters)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind(bindType, name, dict);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind(bindType, name, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindAllTest()
        {
            var parameters = new IInjectionParameter[1];
            Type bindType = typeof(string);
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                        .When(context => context.SpecialParameters == dict && context.Parameters == parameters)
                        .NamedBinding(name);
                injector.Bind<string>().ToConstant(string.Empty)
                    .When(context => context.SpecialParameters == dict && context.Parameters == parameters);
                injector.Bind<string>().ToConstant(string.Empty);

                injector.Bindings.Count.ShouldEqual(3);
                injector.Unbind(bindType);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoWithParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            Type bindType = typeof(string);

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                        .ToConstant(string.Empty)
                        .When(context =>
                              context.Parameters == parameters && context.TypeInto == TypeInto.AsType() &&
                              context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, TypeInto, ParameterInfo, null, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoWithSpecialParametersTest()
        {
            var dict = new Dictionary<string, object>();
            Type bindType = typeof(string);

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.SpecialParameters == dict && context.TypeInto == TypeInto.AsType() &&
                        context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, TypeInto, ParameterInfo, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoWithSpecialParametersAndParametersTest()
        {
            var dict = new Dictionary<string, object>();
            Type bindType = typeof(string);
            var parameters = new IInjectionParameter[1];

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.SpecialParameters == dict && context.Parameters == parameters &&
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, TypeInto, ParameterInfo, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoNamedBindingTest()
        {
            Type bindType = typeof(string);
            const string name = "test";

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(context => context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, name, TypeInto, ParameterInfo);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoNamedBindingAndSpecialParametersTest()
        {
            Type bindType = typeof(string);
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo &&
                        context.SpecialParameters == dict)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, name, TypeInto, ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, name, TypeInto, ParameterInfo, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void NonGenericUnbindIntoNamedBindingAndSpecialParametersAndParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            Type bindType = typeof(string);
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo &&
                        context.SpecialParameters == dict && context.Parameters == parameters)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto(bindType, name, TypeInto, ParameterInfo, dict);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto(bindType, name, TypeInto, ParameterInfo, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindWithParametersTest()
        {
            var parameters = new IInjectionParameter[1];

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(context => context.Parameters == parameters);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(new IInjectionParameter[1]);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindWithSpecialParametersTest()
        {
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(context => context.SpecialParameters == dict);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindWithSpecialParametersAndParametersTest()
        {
            var dict = new Dictionary<string, object>();
            var parameters = new IInjectionParameter[1];

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).When(
                    context => context.SpecialParameters == dict && context.Parameters == parameters);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindNamedBindingTest()
        {
            const string name = "test";

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty).NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(new Dictionary<string, object>());
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(name);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindNamedBindingAndSpecialParametersTest()
        {
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                        .When(context => context.SpecialParameters == dict)
                        .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(name);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(name, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindNamedBindingAndSpecialParametersAndParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                    .When(context => context.SpecialParameters == dict && context.Parameters == parameters)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.Unbind<string>(name, dict);
                injector.Bindings.Count.ShouldEqual(1);

                injector.Unbind<string>(name, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindAllTest()
        {
            var parameters = new IInjectionParameter[1];
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>().ToConstant(string.Empty)
                        .When(context => context.SpecialParameters == dict && context.Parameters == parameters)
                        .NamedBinding(name);
                injector.Bind<string>().ToConstant(string.Empty)
                    .When(context => context.SpecialParameters == dict && context.Parameters == parameters);
                injector.Bind<string>().ToConstant(string.Empty);

                injector.Bindings.Count.ShouldEqual(3);
                injector.Unbind<string>();
                injector.Bindings.ShouldBeEmpty();
            }
        }
        
        [TestMethod]
        public void GenericUnbindIntoWithParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            
            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                        .ToConstant(string.Empty)
                        .When(context =>
                              context.Parameters == parameters && context.TypeInto == TypeInto.AsType() &&
                              context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(TypeInto, ParameterInfo, null, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindIntoWithSpecialParametersTest()
        {
            var dict = new Dictionary<string, object>();
            
            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.SpecialParameters == dict && context.TypeInto == TypeInto.AsType() &&
                        context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(TypeInto, ParameterInfo, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindIntoWithSpecialParametersAndParametersTest()
        {
            var dict = new Dictionary<string, object>();
            var parameters = new IInjectionParameter[1];

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.SpecialParameters == dict && context.Parameters == parameters &&
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(TypeInto, ParameterInfo, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindIntoNamedBindingTest()
        {
            const string name = "test";

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(context => context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(TypeInto);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(name, TypeInto, ParameterInfo);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindIntoNamedBindingAndSpecialParametersTest()
        {
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo &&
                        context.SpecialParameters == dict)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(name, TypeInto, ParameterInfo);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(name, TypeInto, ParameterInfo, dict);
                injector.Bindings.ShouldBeEmpty();
            }
        }

        [TestMethod]
        public void GenericUnbindIntoNamedBindingAndSpecialParametersAndParametersTest()
        {
            var parameters = new IInjectionParameter[1];
            const string name = "test";
            var dict = new Dictionary<string, object>();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<string>()
                    .ToConstant(string.Empty)
                    .When(
                        context =>
                        context.TypeInto == TypeInto.AsType() && context.ParameterInfo == ParameterInfo &&
                        context.SpecialParameters == dict && context.Parameters == parameters)
                    .NamedBinding(name);
                injector.Bindings.Count.ShouldEqual(1);
                injector.UnbindInto<string>(name, TypeInto, ParameterInfo, dict);
                injector.Bindings.Count.ShouldEqual(1);

                injector.UnbindInto<string>(name, TypeInto, ParameterInfo, dict, parameters);
                injector.Bindings.ShouldBeEmpty();
            }
        }
    }
}